package com.kenny.spring;

import com.kenny.utils.AssertReadable;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.naming.InitialContext;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * KennyApplicationContext.java
 * created by kenny 2022年12月10日 17:29
 */
@Slf4j
public class KennyApplicationContext {

    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());

    private Class configClass;

    @SneakyThrows
    public KennyApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 扫描配文件夹 获取 beandefintion 得到 beandefintionMap
        scan(configClass);

        // 初始化singleton类型的bean对象
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            if (beanDefinition.getScope().equals("singleton")) {
                // 单例
                Object bean = this.createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        });

        //

    }

    @SneakyThrows
    private void scan(Class configClass) {
        // 解析配置类，获取配置类的扫描路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 得到扫描路径
        String path = componentScanAnnotation.value();
        AssertReadable.hasText(path, "得到的扫描路径为空");
        // 获取该路径下的所有类
        ClassLoader classLoader = KennyApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path.replace(".", "/"));
        AssertReadable.isNull(resource, "获取该路径下的所有类路径为空");
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null || files.length < 1) {
                log.info("扫描目录为空");
                return;
            }
            for (File f : files) {
                String filePath = f.getAbsolutePath();
                String classesNameWithPackageName = filePath.substring(filePath.indexOf("classes") + 8, filePath.indexOf(".class")).replace("\\", ".");

                Class<?> clazz = classLoader.loadClass(classesNameWithPackageName);
                if (clazz.isAnnotationPresent(Component.class)) {
                    // 表示该类时个bean对象创建bean对象

                    if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                        BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                        beanPostProcessors.add(beanPostProcessor);
                    }

                    Component component = clazz.getDeclaredAnnotation(Component.class);
                    String beanName = component.value();
                    // 判断当前bean是否是单例bean
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(clazz);
                    if (clazz.isAnnotationPresent(Scope.class)) {
                        Scope scope = clazz.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(scope.value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }
                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            }
        }
    }

    @SneakyThrows
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();


        Object instance = clazz.newInstance();
        // 依赖注入
        for (Field declaredField : clazz.getDeclaredFields()) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                Object autowiredBean = getBean(declaredField.getName());
                declaredField.setAccessible(true);
                declaredField.set(instance, autowiredBean);
            }
        }

        // BeanPostProcessor
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }

        // 初始化
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }

        // BeanPostProcessor
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }


        return instance;
    }

    public Object getBean(String beanName) {
        AssertReadable.isTrue(beanDefinitionMap.containsKey(beanName), "beanName对应的bean不存在");
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (Objects.equals(beanDefinition.getScope(), "singleton")) {
            return singletonObjects.get(beanName);
        } else {
            return this.createBean(beanName, beanDefinition);
        }
    }
}
